/*
 * File    : TRTrackerServerFactoryImpl.java
 * Created : 13-Dec-2003
 * By      : parg
 * 
 * Azureus - a Java Bittorrent client
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details ( see the LICENSE file ).
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.gudy.azureus2.core3.tracker.server.impl;

/**
 * @author parg
 *
 */

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.tracker.server.TRTrackerServer;
import org.gudy.azureus2.core3.tracker.server.TRTrackerServerException;
import org.gudy.azureus2.core3.tracker.server.TRTrackerServerFactory;
import org.gudy.azureus2.core3.tracker.server.TRTrackerServerFactoryListener;
import org.gudy.azureus2.core3.tracker.server.TRTrackerServerStats;
import org.gudy.azureus2.core3.tracker.server.impl.dht.TRTrackerServerDHT;
import org.gudy.azureus2.core3.tracker.server.impl.tcp.TRTrackerServerTCP;
import org.gudy.azureus2.core3.tracker.server.impl.tcp.blocking.TRBlockingServer;
import org.gudy.azureus2.core3.tracker.server.impl.tcp.nonblocking.TRNonBlockingServer;
import org.gudy.azureus2.core3.tracker.server.impl.tcp.nonblocking.TRNonBlockingServerProcessor;
import org.gudy.azureus2.core3.tracker.server.impl.tcp.nonblocking.TRNonBlockingServerProcessorFactory;
import org.gudy.azureus2.core3.tracker.server.impl.udp.TRTrackerServerUDP;
import org.gudy.azureus2.core3.util.AEMonitor;
import org.gudy.azureus2.core3.util.AsyncController;

import com.aelitis.azureus.core.stats.AzureusCoreStats;
import com.aelitis.azureus.core.stats.AzureusCoreStatsProvider;
import com.aelitis.azureus.core.util.CopyOnWriteList;

public class TRTrackerServerFactoryImpl {
    protected static CopyOnWriteList servers = new CopyOnWriteList();

    protected static List listeners = new ArrayList();
    protected static AEMonitor class_mon = new AEMonitor("TRTrackerServerFactory");

    static {
        Set types = new HashSet();

        types.add(AzureusCoreStats.ST_TRACKER_READ_BYTES);
        types.add(AzureusCoreStats.ST_TRACKER_WRITE_BYTES);
        types.add(AzureusCoreStats.ST_TRACKER_ANNOUNCE_COUNT);
        types.add(AzureusCoreStats.ST_TRACKER_ANNOUNCE_TIME);
        types.add(AzureusCoreStats.ST_TRACKER_SCRAPE_COUNT);
        types.add(AzureusCoreStats.ST_TRACKER_SCRAPE_TIME);

        AzureusCoreStats.registerProvider(types, new AzureusCoreStatsProvider() {
            public void updateStats(Set types, Map values) {
                long read_bytes = 0;
                long write_bytes = 0;
                long announce_count = 0;
                long announce_time = 0;
                long scrape_count = 0;
                long scrape_time = 0;

                Iterator it = servers.iterator();

                while (it.hasNext()) {

                    TRTrackerServerStats stats = ((TRTrackerServer) it.next()).getStats();

                    read_bytes += stats.getBytesIn();
                    write_bytes += stats.getBytesOut();
                    announce_count += stats.getAnnounceCount();
                    announce_time += stats.getAnnounceTime();
                    scrape_count += stats.getScrapeCount();
                    scrape_time += stats.getScrapeTime();
                }

                if (types.contains(AzureusCoreStats.ST_TRACKER_READ_BYTES)) {

                    values.put(AzureusCoreStats.ST_TRACKER_READ_BYTES, new Long(read_bytes));
                }
                if (types.contains(AzureusCoreStats.ST_TRACKER_WRITE_BYTES)) {

                    values.put(AzureusCoreStats.ST_TRACKER_WRITE_BYTES, new Long(write_bytes));
                }
                if (types.contains(AzureusCoreStats.ST_TRACKER_ANNOUNCE_COUNT)) {

                    values.put(AzureusCoreStats.ST_TRACKER_ANNOUNCE_COUNT, new Long(announce_count));
                }
                if (types.contains(AzureusCoreStats.ST_TRACKER_ANNOUNCE_TIME)) {

                    values.put(AzureusCoreStats.ST_TRACKER_ANNOUNCE_TIME, new Long(announce_time));
                }
                if (types.contains(AzureusCoreStats.ST_TRACKER_SCRAPE_COUNT)) {

                    values.put(AzureusCoreStats.ST_TRACKER_SCRAPE_COUNT, new Long(scrape_count));
                }
                if (types.contains(AzureusCoreStats.ST_TRACKER_SCRAPE_TIME)) {

                    values.put(AzureusCoreStats.ST_TRACKER_SCRAPE_TIME, new Long(scrape_time));
                }
            }
        });
    }

    public static TRTrackerServer create(String name, int protocol, int port, InetAddress bind_ip, boolean ssl, boolean apply_ip_filter,
            boolean main_tracker, boolean start_up_ready)

    throws TRTrackerServerException {
        try {
            class_mon.enter();

            TRTrackerServerImpl server;

            if (protocol == TRTrackerServerFactory.PR_TCP) {

                if (COConfigurationManager.getBooleanParameter("Tracker TCP NonBlocking") && main_tracker && !ssl) {

                    server =
                            new TRNonBlockingServer(name, port, bind_ip, apply_ip_filter, start_up_ready,
                                    new TRNonBlockingServerProcessorFactory() {
                                        public TRNonBlockingServerProcessor create(TRTrackerServerTCP _server, SocketChannel _socket) {
                                            return (new NonBlockingProcessor(_server, _socket));

                                        }
                                    });
                } else {

                    server = new TRBlockingServer(name, port, bind_ip, ssl, apply_ip_filter, start_up_ready);
                }

            } else if (protocol == TRTrackerServerFactory.PR_UDP) {

                if (ssl) {

                    throw (new TRTrackerServerException("TRTrackerServerFactory: UDP doesn't support SSL"));
                }

                server = new TRTrackerServerUDP(name, port, start_up_ready);

            } else {

                server = new TRTrackerServerDHT(name, start_up_ready);
            }

            servers.add(server);

            for (int i = 0; i < listeners.size(); i++) {

                ((TRTrackerServerFactoryListener) listeners.get(i)).serverCreated(server);
            }

            return (server);

        } finally {

            class_mon.exit();
        }
    }

    protected static void close(TRTrackerServerImpl server) {
        try {
            class_mon.enter();

            server.closeSupport();

            if (servers.remove(server)) {

                for (int i = 0; i < listeners.size(); i++) {

                    ((TRTrackerServerFactoryListener) listeners.get(i)).serverDestroyed(server);
                }
            }
        } finally {

            class_mon.exit();
        }
    }

    public static void addListener(TRTrackerServerFactoryListener l) {
        try {
            class_mon.enter();

            listeners.add(l);

            Iterator it = servers.iterator();

            while (it.hasNext()) {

                l.serverCreated((TRTrackerServer) it.next());
            }
        } finally {

            class_mon.exit();
        }
    }

    public static void removeListener(TRTrackerServerFactoryListener l) {
        try {
            class_mon.enter();

            listeners.remove(l);

        } finally {

            class_mon.exit();
        }
    }

    protected static class NonBlockingProcessor extends TRNonBlockingServerProcessor {
        protected NonBlockingProcessor(TRTrackerServerTCP _server, SocketChannel _socket) {
            super(_server, _socket);
        }

        protected ByteArrayOutputStream process(String input_header, String lowercase_input_header, String url_path,
                InetSocketAddress remote_address, boolean announce_and_scrape_only, InputStream is, AsyncController async)

        throws IOException {
            ByteArrayOutputStream os = new ByteArrayOutputStream(1024);

            InetSocketAddress local_address = null; // TODO

            processRequest(input_header, lowercase_input_header, url_path, local_address, remote_address, announce_and_scrape_only, false, is, os,
                    async);

            return (os);
        }
    }
}
